package checker

import (
	"kumachan/interpreter/lang/ast"
	"kumachan/interpreter/lang/common/name"
	"kumachan/interpreter/compiler/checker/typsys"
	"kumachan/interpreter/compiler/checker/checked"
)


type ExprContext struct {
	*ExprContextBase
	LocalBindingMap  LocalBindingMap
}
type ExprContextBase struct {
	*Registry
	*ModuleInfo
	ParameterList    [] typsys.Parameter
	DispatchMapping  DispatchMapping
}
type Registry struct {
	Aliases    AliasRegistry
	Types      TypeRegistry
	Functions  FunctionRegistry
}
type LocalBindingMap (map[string] *checked.LocalBinding)

func (lm LocalBindingMap) clone() LocalBindingMap {
	var clone = make(LocalBindingMap)
	for k, v := range lm {
		clone[k] = v
	}
	return clone
}
func (lm LocalBindingMap) add(binding *checked.LocalBinding) {
	lm[binding.Name] = binding
}
func (lm LocalBindingMap) lookup(name string) (*checked.LocalBinding, bool) {
	var binding, exists = lm[name]
	return binding, exists
}

func (reg *Registry) lookupFuncRefs(n name.Name, mod string) (FuncRefs, bool) {
	if n.ModuleName == "" {
		if mod == CoreModule {
			return reg.lookupFuncRefs(name.MakeName(mod, n.ItemName), mod)
		} else {
			var mod_name = name.MakeName(mod, n.ItemName)
			var core_name = name.MakeName(CoreModule, n.ItemName)
			var mod_refs, mod_ok = reg.lookupFuncRefs(mod_name, mod)
			var core_refs, core_ok = reg.lookupFuncRefs(core_name, mod)
			if mod_ok || core_ok {
				var refs FuncRefs
				var functions = &(refs.Functions)
				*functions = append(*functions, mod_refs.Functions...)
				*functions = append(*functions, core_refs.Functions...)
				return refs, true
			} else {
				return FuncRefs {}, false
			}
		}
	}
	var alias, is_alias = reg.Aliases[n]
	if is_alias {
		return reg.lookupFuncRefs(alias.To, mod)
	} else {
		var functions, exists = reg.Functions[n]
		if exists {
			if n.ModuleName == mod {
				return FuncRefs { Functions: functions }, true
			} else {
				var available = make([] *Function, 0)
				for _, f := range functions {
					if f.Exported && f.AstNode.Kind != ast.FK_Do {
						available = append(available, f)
					}
				}
				if len(available) > 0 {
					return FuncRefs { Functions: available }, true
				} else {
					return FuncRefs {}, false
				}
			}
		} else {
			return FuncRefs {}, false
		}
	}
}

func (reg *Registry) lookupRef(n name.Name, mod string, lm LocalBindingMap) (Ref, bool) {
	if n.ModuleName == "" {
		var binding, local_exists = lm[n.ItemName]
		if local_exists {
			var local = LocalRef { Binding: binding }
			var f, f_exists = reg.lookupFuncRefs(n, mod)
			if f_exists {
				return LocalRefWithFuncRefs {
					LocalRef: local,
					FuncRefs: f,
				}, true
			} else {
				return local, true
			}
		} else {
			return reg.lookupFuncRefs(n, mod)
		}
	} else {
		return reg.lookupFuncRefs(n, mod)
	}
}

func (ctx ExprContext) withNewLocalScope() ExprContext {
	return ExprContext {
		ExprContextBase: ctx.ExprContextBase,
		LocalBindingMap: ctx.LocalBindingMap.clone(),
	}
}
func (ctx ExprContext) makeAssignContext(s *typsys.InferringState) typsys.AssignContext {
	return typsys.MakeAssignContext(ctx.ModName, s)
}
